
var Message = {};

Message.TYPE_REQUEST = 0;
Message.TYPE_NOTIFY = 1;
Message.TYPE_RESPONSE = 2;
Message.TYPE_PUSH = 3;

Message.encode = function(id,type,compressRoute,route,msg) {
    // caculate message max length
    var idBytes = msgHasId(type) ? caculateMsgIdBytes(id) : 0;
    var msgLen = MSG_FLAG_BYTES + idBytes;

    if (msgHasRoute(type)) {
        if (compressRoute) {
            if (typeof route !== 'number') {
                throw new Error('error flag for number route!');
            }
            msgLen += MSG_ROUTE_CODE_BYTES;
        } else {
            msgLen += MSG_ROUTE_LEN_BYTES;
            if (route) {
                route = Protocol.strencode(route);
                if (route.length > 255) {
                    throw new Error('route maxlength is overflow');
                }
                msgLen += route.length;
            }
        }
    }

    if (msg) {
        msgLen += msg.length;
    }

    var buffer = new Uint8Array(msgLen);
    var offset = 0;

    // add flag
    offset = encodeMsgFlag(type,compressRoute,buffer,offset);

    // add message id
    if (msgHasId(type)) {
        offset = encodeMsgId(id,idBytes,buffer,offset);
    }

    // add route
    if (msgHasRoute(type)) {
        offset = encodeMsgRoute(compressRoute,route,buffer,offset);
    }

    // add body
    if (msg) {
        offset = encodeMsgBody(msg,buffer,offset);
    }

    return buffer;
};

/**
 * Message protocol decode.
 *
 * @param  {Buffer|Uint8Array} buffer message bytes
 * @return {Object}            message object
 */
Message.decode = function(buffer) {
    var bytes = new Uint8Array(buffer);
    var bytesLen = bytes.length || bytes.byteLength;
    var offset = 0;
    var id = 0;
    var route = null;

    // parse flag
    var flag = bytes[offset++];
    var compressRoute = flag & MSG_COMPRESS_ROUTE_MASK;
    var type = (flag >> 1) & MSG_TYPE_MASK;

    // parse id
    if (msgHasId(type)) {
        var byte = bytes[offset++];
        id = byte & 0x7f;
        while (byte & 0x80) {
            id <<= 7;
            byte = bytes[offset++];
            id |= byte & 0x7f;
        }
    }

    // parse route
    if (msgHasRoute(type)) {
        if (compressRoute) {
            route = (bytes[offset++]) << 8 | bytes[offset++];
        } else {
            var routeLen = bytes[offset++];
            if (routeLen) {
                route = new Uint8Array(routeLen);
                copyArray(route,0,bytes,offset,routeLen);
                route = Protocol.strdecode(route);
            } else {
                route = '';
            }
            offset += routeLen;
        }
    }

    // parse body
    var bodyLen = bytesLen - offset;
    var body = new Uint8Array(bodyLen);

    copyArray(body,0,bytes,offset,bodyLen);

    return {
        'id': id,
        'type': type,
        'compressRoute': compressRoute,
        'route': route,
        'body': body
    };
};
